This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.

ds2 <- readRDS("ds2.rds")

umapplot

ds0 <- readRDS("ds0.rds")

周细胞的存在

library(igraph)

基因表达

ds2 -> ds1 无监督聚类


ds2data <- get_data_table(ds2FbM,type = "data")
ds1data <- get_data_table(ds1FbM,type = "data")

# ds2expr <- data.frame(expr = ds2data["DCN",], sample = "ds2", gene = "DCN")
# rownames(ds2expr) <- NULL
# ds1expr <- data.frame(expr = ds1data["DCN",], sample = "ds1")
# rownames(ds1expr) <- NULL
# merge_expr <- rbind(ds2expr,ds1expr)

genes_to_show <- c("DCN","LUM","MMP2","ACTA2","TNFRSF11B","FBLN1")

func <- function(gene, sample, datable){
  data.frame(expr = datable[gene,], sample = sample, gene = gene)
}

merge_expr <- data.frame()

for (i in lapply(genes_to_show, func,"ds1",ds1data))
{
  merge_expr <- rbind(merge_expr,i)
}
for (i in lapply(genes_to_show, func,"ds2",ds2data))
{
  merge_expr <- rbind(merge_expr,i)
}


rownames(merge_expr) <- NULL



Data_summary <- Rmisc::summarySE(merge_expr, measurevar="expr", groupvars=c("sample","gene"))
head(Data_summary)


ggobj <- ggplot(merge_expr,aes(x = gene, y = expr,fill = sample)) +
  geom_split_violin(trim= F, color="white", scale = "area") + 
  geom_point(data = Data_summary,aes(x = gene, y= expr), pch=19,
             position=position_dodge(0.2),size= 1) + #绘制均值位置
  geom_errorbar(data = Data_summary, aes(ymin = expr-ci, ymax= expr+ci), 
                width= 0.05, 
                position= position_dodge(0.2), #误差线位置,和均值位置相匹配
                color="black",
                alpha = 0.7,
                size= 0.5) +
  scale_fill_manual(values = c("#b1d6fb", "#fd9999"))+ 
  labs(y=("Log2 expression"),x=NULL,title = "Split violin") + 
  theme_classic()+ mytheme + stat_compare_means(aes(group = sample),
                     label = "p.format",
                     method = "wilcox.test",
                     label.y = max(merge_expr$expr),
                      hide.ns = F)
ggsave("unds2tods1.svg", device = svg, plot = ggobj, height = 6, width = 10)

监督聚类结果

这里使用numeric的分群

ds2FbM <- subset(ds2,ident = "1")
ds1FbM <- subset(ds1,ident = "1")

ds2data <- get_data_table(ds2FbM,type = "data")
ds1data <- get_data_table(ds1FbM,type = "data")

# ds2expr <- data.frame(expr = ds2data["DCN",], sample = "ds2", gene = "DCN")
# rownames(ds2expr) <- NULL
# ds1expr <- data.frame(expr = ds1data["DCN",], sample = "ds1")
# rownames(ds1expr) <- NULL
# merge_expr <- rbind(ds2expr,ds1expr)

genes_to_show <- c("DCN","LUM","MMP2","ACTA2","TNFRSF11B","FBLN1")

func1 <- function(gene, sample, datable){
  data.frame(expr = datable[gene,], sample = sample, gene = gene)
}

merge_expr <- data.frame()

for (i in lapply(genes_to_show, func1,"ds1",ds1data))
{
  merge_expr <- rbind(merge_expr,i)
}
for (i in lapply(genes_to_show, func1,"ds2",ds2data))
{
  merge_expr <- rbind(merge_expr,i)
}


rownames(merge_expr) <- NULL

Data_summary <- Rmisc::summarySE(merge_expr, measurevar="expr", groupvars=c("sample","gene"))
head(Data_summary)

ggobj <- ggplot(merge_expr,aes(x = gene, y = expr,fill = sample)) +
  geom_split_violin(trim= F, color="white", scale = "area") + 
  geom_point(data = Data_summary,aes(x = gene, y= expr), pch=19,
             position=position_dodge(0.2),size= 1) + #绘制均值位置
  geom_errorbar(data = Data_summary, aes(ymin = expr-ci, ymax= expr+ci), 
                width= 0.05, 
                position= position_dodge(0.2), #误差线位置,和均值位置相匹配
                color="black",
                alpha = 0.7,
                size= 0.5) +
  scale_fill_manual(values = c("#b1d6fb", "#fd9999"))+ 
  labs(y=("Log2 expression"),x=NULL,title = "Split violin") + 
  theme_classic()+ mytheme + stat_compare_means(aes(group = sample),
                     label = "p.format",
                     method = "wilcox.test",
                     label.y = max(merge_expr$expr),
                      hide.ns = F)

ggsave("supds2tods1.svg", device = svg, plot = ggobj, height = 6, width = 10)

ds2 -> ds0

无监督聚类


ds2data <- get_data_table(ds2FbM,type = "data")
ds0data <- get_data_table(ds0FbM,type = "data")

genes_to_show <- c("DCN","LUM","MMP2","ACTA2","TNFRSF11B","FBLN1")

func <- function(gene, sample, datable){
  data.frame(expr = datable[gene,], sample = sample, gene = gene)
}

merge_expr <- data.frame()

for (i in lapply(genes_to_show, func,"ds0",ds0data))
{
  merge_expr <- rbind(merge_expr,i)
}
for (i in lapply(genes_to_show, func,"ds2",ds2data))
{
  merge_expr <- rbind(merge_expr,i)
}

rownames(merge_expr) <- NULL

Data_summary <- Rmisc::summarySE(merge_expr, measurevar="expr", groupvars=c("sample","gene"))
head(Data_summary)


ggobj <- ggplot(merge_expr,aes(x = gene, y = expr,fill = sample)) +
  geom_split_violin(trim= F, color="white", scale = "area") + 
  geom_point(data = Data_summary,aes(x = gene, y= expr), pch=19,
             position=position_dodge(0.2),size= 1) + #绘制均值位置
  geom_errorbar(data = Data_summary, aes(ymin = expr-ci, ymax= expr+ci), 
                width= 0.05, 
                position= position_dodge(0.2), #误差线位置,和均值位置相匹配
                color="black",
                alpha = 0.7,
                size= 0.5) +
  scale_fill_manual(values = c("#b1d6fb", "#fd9999"))+ 
  labs(y=("Log2 expression"),x=NULL,title = "Split violin") + 
  theme_classic()+ mytheme + stat_compare_means(aes(group = sample),
                     label = "p.format",
                     method = "wilcox.test",
                     label.y = max(merge_expr$expr),
                      hide.ns = F)

监督聚类

ds2data <- get_data_table(ds2FbM,type = "data")
ds0data <- get_data_table(ds0FbM,type = "data")

genes_to_show <- c("DCN","LUM","MMP2","ACTA2","TNFRSF11B","FBLN1")

func <- function(gene, sample, datable){
  data.frame(expr = datable[gene,], sample = sample, gene = gene)
}

merge_expr <- data.frame()

for (i in lapply(genes_to_show, func,"ds0",ds0data))
{
  merge_expr <- rbind(merge_expr,i)
}
for (i in lapply(genes_to_show, func,"ds2",ds2data))
{
  merge_expr <- rbind(merge_expr,i)
}

rownames(merge_expr) <- NULL

Data_summary <- Rmisc::summarySE(merge_expr, measurevar="expr", groupvars=c("sample","gene"))
head(Data_summary)


ggobj <- ggplot(merge_expr,aes(x = gene, y = expr,fill = sample)) +
  geom_split_violin(trim= F, color="white", scale = "area") + 
  geom_point(data = Data_summary,aes(x = gene, y= expr), pch=19,
             position=position_dodge(0.2),size= 1) + #绘制均值位置
  geom_errorbar(data = Data_summary, aes(ymin = expr-ci, ymax= expr+ci), 
                width= 0.05, 
                position= position_dodge(0.2), #误差线位置,和均值位置相匹配
                color="black",
                alpha = 0.7,
                size= 0.5) +
  scale_fill_manual(values = c("#b1d6fb", "#fd9999"))+ 
  labs(y=("Log2 expression"),x=NULL,title = "Split violin") + 
  theme_classic()+ mytheme + stat_compare_means(aes(group = sample),
                     label = "p.format",
                     method = "wilcox.test",
                     label.y = max(merge_expr$expr),
                      hide.ns = F)

ggsave("supds2tods0.svg", device = svg, plot = ggobj, height = 6, width = 10)

ds0 -> ds2

##无监督聚类


ds2data <- get_data_table(ds2FbM,type = "data")
ds0data <- get_data_table(ds0FbM,type = "data")

# genes_to_show <- c("IGFBP2","MGP","MYH11","DCN","TNFRSF11B")
genes_to_show <- c("DCN","LUM","MMP2","ACTA2","TNFRSF11B","FBLN1")

func <- function(gene, sample, datable){
  data.frame(expr = datable[gene,], sample = sample, gene = gene)
}

merge_expr <- data.frame()

for (i in lapply(genes_to_show, func,"ds0",ds0data))
{
  merge_expr <- rbind(merge_expr,i)
}
for (i in lapply(genes_to_show, func,"ds2",ds2data))
{
  merge_expr <- rbind(merge_expr,i)
}

rownames(merge_expr) <- NULL

Data_summary <- Rmisc::summarySE(merge_expr, measurevar="expr", groupvars=c("sample","gene"))
head(Data_summary)


ggobj <- ggplot(merge_expr,aes(x = gene, y = expr,fill = sample)) +
  geom_split_violin(trim= F, color="white", scale = "area") + 
  geom_point(data = Data_summary,aes(x = gene, y= expr), pch=19,
             position=position_dodge(0.2),size= 1) + #绘制均值位置
  geom_errorbar(data = Data_summary, aes(ymin = expr-ci, ymax= expr+ci), 
                width= 0.05, 
                position= position_dodge(0.2), #误差线位置,和均值位置相匹配
                color="black",
                alpha = 0.7,
                size= 0.5) +
  scale_fill_manual(values = c("#b1d6fb", "#fd9999"))+ 
  labs(y=("Log2 expression"),x=NULL,title = "Split violin") + 
  theme_classic()+ mytheme + stat_compare_means(aes(group = sample),
                     label = "p.format",
                     method = "wilcox.test",
                     label.y = max(merge_expr$expr),
                      hide.ns = F)

ggsave("2_unds0tods2.svg", device = svg, plot = ggobj, height = 6, width = 10)

监督聚类

ds0 -> ds2


func <- function(gene, sample, datable){
  data.frame(expr = datable[gene,], sample = sample, gene = gene)
}

merge_expr <- data.frame()

for (i in lapply(genes_to_show, func,"ds0",ds0data))
{
  merge_expr <- rbind(merge_expr,i)
}
for (i in lapply(genes_to_show, func,"ds2",ds2data))
{
  merge_expr <- rbind(merge_expr,i)
}

rownames(merge_expr) <- NULL

Data_summary <- Rmisc::summarySE(merge_expr, measurevar="expr", groupvars=c("sample","gene"))
head(Data_summary)


ggobj <- ggplot(merge_expr,aes(x = gene, y = expr,fill = sample)) +
  geom_split_violin(trim= F, color="white", scale = "area") + 
  geom_point(data = Data_summary,aes(x = gene, y= expr), pch=19,
             position=position_dodge(0.2),size= 1) + #绘制均值位置
  geom_errorbar(data = Data_summary, aes(ymin = expr-ci, ymax= expr+ci), 
                width= 0.05, 
                position= position_dodge(0.2), #误差线位置,和均值位置相匹配
                color="black",
                alpha = 0.7,
                size= 0.5) +
  scale_fill_manual(values = c("#b1d6fb", "#fd9999"))+ 
  labs(y=("Log2 expression"),x=NULL,title = "Split violin") + 
  theme_classic()+ mytheme + stat_compare_means(aes(group = sample),
                     label = "p.format",
                     method = "wilcox.test",
                     label.y = max(merge_expr$expr),
                      hide.ns = F)
ggobj
ggsave("2_supds0tods2.svg", device = svg, plot = ggobj, height = 6, width = 10)

ds0 -> ds1

##无监督聚类

genes_to_show <- c("DCN","LUM","MMP2","ACTA2","TNFRSF11B","FBLN1")

func <- function(gene, sample, datable){
  data.frame(expr = datable[gene,], sample = sample, gene = gene)
}

merge_expr <- data.frame()

for (i in lapply(genes_to_show, func,"ds0",ds0data))
{
  merge_expr <- rbind(merge_expr,i)
}
for (i in lapply(genes_to_show, func,"ds1",ds1data))
{
  merge_expr <- rbind(merge_expr,i)
}

rownames(merge_expr) <- NULL

Data_summary <- Rmisc::summarySE(merge_expr, measurevar="expr", groupvars=c("sample","gene"))
head(Data_summary)


ggobj <- ggplot(merge_expr,aes(x = gene, y = expr,fill = sample)) +
  geom_split_violin(trim= F, color="white", scale = "area") + 
  geom_point(data = Data_summary,aes(x = gene, y= expr), pch=19,
             position=position_dodge(0.2),size= 1) + #绘制均值位置
  geom_errorbar(data = Data_summary, aes(ymin = expr-ci, ymax= expr+ci), 
                width= 0.05, 
                position= position_dodge(0.2), #误差线位置,和均值位置相匹配
                color="black",
                alpha = 0.7,
                size= 0.5) +
  scale_fill_manual(values = c("#b1d6fb", "#fd9999"))+ 
  labs(y=("Log2 expression"),x=NULL,title = "Split violin") + 
  theme_classic()+ mytheme + stat_compare_means(aes(group = sample),
                     label = "p.format",
                     method = "wilcox.test",
                     label.y = max(merge_expr$expr),
                      hide.ns = F)
ggobj
ggsave("2_unds0tods1.svg", device = svg, plot = ggobj, height = 6, width = 10)

监督聚类

ds0 -> ds1

merge_expr <- data.frame()

for (i in lapply(genes_to_show, func1,"ds0",ds0data))
{
  merge_expr <- rbind(merge_expr,i)
}
for (i in lapply(genes_to_show, func1,"ds1",ds1data))
{
  merge_expr <- rbind(merge_expr,i)
}

rownames(merge_expr) <- NULL

Data_summary <- Rmisc::summarySE(merge_expr, measurevar="expr", groupvars=c("sample","gene"))
head(Data_summary)


ggobj <- ggplot(merge_expr,aes(x = gene, y = expr,fill = sample)) +
  geom_split_violin(trim= F, color="white", scale = "area") + 
  geom_point(data = Data_summary,aes(x = gene, y= expr), pch=19,
             position=position_dodge(0.2),size= 1) + #绘制均值位置
  geom_errorbar(data = Data_summary, aes(ymin = expr-ci, ymax= expr+ci), 
                width= 0.05, 
                position= position_dodge(0.2), #误差线位置,和均值位置相匹配
                color="black",
                alpha = 0.7,
                size= 0.5) +
  scale_fill_manual(values = c("#b1d6fb", "#fd9999"))+ 
  labs(y=("Log2 expression"),x=NULL,title = "Split violin") + 
  theme_classic()+ mytheme + stat_compare_means(aes(group = sample),
                     label = "p.format",
                     method = "wilcox.test",
                     label.y = max(merge_expr$expr),
                      hide.ns = F)
ggobj

mytheme <- theme(plot.title = element_text(size = 12,color="black",hjust = 0.5),
                     axis.title = element_text(size = 12,color ="black"), 
                     axis.text = element_text(size= 12,color = "black"),
                     panel.grid.minor.y = element_blank(),
                     panel.grid.minor.x = element_blank(),
                     axis.text.x = element_text(angle = 45, hjust = 1 ),
                     panel.grid=element_blank(),
                     legend.position = "top",
                     legend.text = element_text(size= 12),
                     legend.title= element_text(size= 12)) 

# https://stackoverflow.com/a/45614547
GeomSplitViolin <- ggproto("GeomSplitViolin", GeomViolin, draw_group = function(self, data, ..., draw_quantiles = NULL){
  data <- transform(data, xminv = x - violinwidth * (x - xmin), xmaxv = x + violinwidth * (xmax - x))
  grp <- data[1,'group']
  newdata <- plyr::arrange(transform(data, x = if(grp%%2==1) xminv else xmaxv), if(grp%%2==1) y else -y)
  newdata <- rbind(newdata[1, ], newdata, newdata[nrow(newdata), ], newdata[1, ])
  newdata[c(1,nrow(newdata)-1,nrow(newdata)), 'x'] <- round(newdata[1, 'x']) 
  if (length(draw_quantiles) > 0 & !scales::zero_range(range(data$y))) {
    stopifnot(all(draw_quantiles >= 0), all(draw_quantiles <= 
                                              1))
    quantiles <- ggplot2:::create_quantile_segment_frame(data, draw_quantiles)
    aesthetics <- data[rep(1, nrow(quantiles)), setdiff(names(data), c("x", "y")), drop = FALSE]
    aesthetics$alpha <- rep(1, nrow(quantiles))
    both <- cbind(quantiles, aesthetics)
    quantile_grob <- GeomPath$draw_panel(both, ...)
    ggplot2:::ggname("geom_split_violin", grid::grobTree(GeomPolygon$draw_panel(newdata, ...), quantile_grob))
  }
  else {
    ggplot2:::ggname("geom_split_violin", GeomPolygon$draw_panel(newdata, ...))
  }
})

geom_split_violin <- function (mapping = NULL, data = NULL, stat = "ydensity", position = "identity", ..., draw_quantiles = NULL, trim = TRUE, scale = "area", na.rm = FALSE, show.legend = NA, inherit.aes = TRUE) {
  layer(data = data, mapping = mapping, stat = stat, geom = GeomSplitViolin, position = position, show.legend = show.legend, inherit.aes = inherit.aes, params = list(trim = trim, scale = scale, draw_quantiles = draw_quantiles, na.rm = na.rm, ...))
}

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike Knit, Preview does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ3RybCtTaGlmdCtFbnRlciouIAoKYGBge3J9CnNvdXJjZSgidGlhbmZlbmdSd3JhcHBlcnMuUiIpCmBgYApgYGB7cn0KZHMyIDwtIHJlYWRSRFMoImRzMi5yZHMiKQpgYGAKCiMgdW1hcHBsb3QKYGBge3J9CnVtYXBwbG90KGRzMikKZHMyIDwtIFJlbmFtZUlkZW50cyhkczIsIkZCIiA9ICJGaWJyb2JsYXN0IiwgInBlcmljeXRlIiA9ICJQZXJpY3l0ZSIsImZpYnJvbXlvY3l0ZSI9IkZpYnJvbXlvY3l0ZSIpCnVtYXBwbG90KGRzMiwgZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIikKSWRlbnRzKGRzMikgPC0gZmFjdG9yKElkZW50cyhkczIpLGxldmVscyA9IGMoIlNNQzEiLCJGaWJyb215b2N5dGUiLCJQZXJpY3l0ZSIsIkZpYnJvYmxhc3QiLCJTTUMyIikpCnVtYXBwbG90KGRzMikKZHMyJENsYXNzaWZpY2F0aW9uMSA8LSBJZGVudHMoZHMyKQpzYXZlUkRTKGRzMiwiZHMyLnJkcyIpCgpnZ3NhdmUoIi4vZmlnMy9kczIuc3ZnIixwbG90ID0gdW1hcHBsb3QoZHMyKSxkZXZpY2UgPSBzdmcsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNSkKYGBgCgpgYGB7cn0KZHMwIDwtIHJlYWRSRFMoImRzMC5yZHMiKQpgYGAKCiMg5ZGo57uG6IOe55qE5a2Y5ZyoCmBgYHtyIGZpZy53aWR0aD02LGZpZy5oZWlnaHQ9Nn0KCmdnc2F2ZSgiLi9maWczL2RzMC5zdmciLHBsb3QgPSB1bWFwcGxvdChkczApLGRldmljZSA9IHN2Zywgd2lkdGggPSA3LCBoZWlnaHQgPSA1KQoKIyMgcGVyaWN5dGUKbXVsdGlfZmVhdHVyZXBsb3QoYygiRkFCUDQiLCAiUkVSR0wiLCAiTlJJUDIiLCJISUdEMUIiKSxkczIsbGFiZWxzID0gIiIpCmBgYAoKYGBge3J9CmxpYnJhcnkoaWdyYXBoKQpgYGAKCmBgYHtyfQpkczFfZXhwciA8LSBnZXRfYXZlcmV4cHJfbWF0X2NsdXN0ZXIoZHMxKQpjbHVzdGVyX2luZm8gPC0gZGF0YS5mcmFtZShjb2xuYW1lcyhkczFfZXhwciksIjEiLGNoZWNrLm5hbWVzID0gRikKCmRzMl9leHByIDwtIGdldF9hdmVyZXhwcl9tYXRfY2x1c3RlcihkczIpCmNsdXN0ZXJfaW5mbzIgPC0gZGF0YS5mcmFtZShjb2xuYW1lcyhkczJfZXhwciksIjIiLGNoZWNrLm5hbWVzID0gRikKCnNlbGVjdGVkX2dlbmVzIDwtIGludGVyc2VjdChkczJAYXNzYXlzW1siU0NUIl1dQHZhci5mZWF0dXJlcyxkczFAYXNzYXlzW1siU0NUIl1dQHZhci5mZWF0dXJlcykKZHMyX2V4cHIgPC0gZHMyX2V4cHJbc2VsZWN0ZWRfZ2VuZXMsXQpkczFfZXhwciA8LSBkczFfZXhwcltzZWxlY3RlZF9nZW5lcyxdCmV4cHIgPC0gY2JpbmQoZHMxX2V4cHIsZHMyX2V4cHIpCmNvcnJfbWF0IDwtIGNvcihleHByKQoKY29sX2Z1biA8LSAgY29sb3JSYW1wMihjKDAuNywgMC44NSwgMSksIGMoIiMxRTkwRkYiLCAid2hpdGUiLCAiI2ZmMjEyMSIpKQpIZWF0bWFwKGNvcnJfbWF0LCBjbHVzdGVyX3Jvd3MgPSBULCBjbHVzdGVyX2NvbHVtbnMgPSBULCAKICAgICAgICBzaG93X2NvbHVtbl9uYW1lcyA9IFQsIHNob3dfcm93X25hbWVzID0gVCwKIGNvbHVtbl90aXRsZSA9IE5VTEwsCiBoZWF0bWFwX2xlZ2VuZF9wYXJhbSA9IGxpc3QoCiB0aXRsZT0nQ29yJywgdGl0bGVfcG9zaXRpb249J2xlZnRjZW50ZXItcm90JyksIGNvbCA9IGNvbF9mdW4pCmBgYAoKIyMg5Z+65Zug6KGo6L6+CmRzMiAtPiBkczEK5peg55uR552j6IGa57G7CmBgYHtyfQpkczEgPC0gcmVhZFJEUygiZHMxLnJkcyIpCmRzMkZiTSA8LSBzdWJzZXQoZHMyLGlkZW50ID0gIkZpYnJvbXlvY3l0ZSIpCmRzMUZiTSA8LSBzdWJzZXQoZHMxLGlkZW50ID0gIkZpYnJvbXlvY3l0ZSIpCgpkczJkYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMkZiTSx0eXBlID0gImRhdGEiKQpkczFkYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMUZiTSx0eXBlID0gImRhdGEiKQoKIyBkczJleHByIDwtIGRhdGEuZnJhbWUoZXhwciA9IGRzMmRhdGFbIkRDTiIsXSwgc2FtcGxlID0gImRzMiIsIGdlbmUgPSAiRENOIikKIyByb3duYW1lcyhkczJleHByKSA8LSBOVUxMCiMgZHMxZXhwciA8LSBkYXRhLmZyYW1lKGV4cHIgPSBkczFkYXRhWyJEQ04iLF0sIHNhbXBsZSA9ICJkczEiKQojIHJvd25hbWVzKGRzMWV4cHIpIDwtIE5VTEwKIyBtZXJnZV9leHByIDwtIHJiaW5kKGRzMmV4cHIsZHMxZXhwcikKCmdlbmVzX3RvX3Nob3cgPC0gYygiRENOIiwiTFVNIiwiTU1QMiIsIkFDVEEyIiwiVE5GUlNGMTFCIiwiRkJMTjEiKQoKZnVuYzEgPC0gZnVuY3Rpb24oZ2VuZSwgc2FtcGxlLCBkYXRhYmxlKXsKICBkYXRhLmZyYW1lKGV4cHIgPSBkYXRhYmxlW2dlbmUsXSwgc2FtcGxlID0gc2FtcGxlLCBnZW5lID0gZ2VuZSkKfQoKbWVyZ2VfZXhwciA8LSBkYXRhLmZyYW1lKCkKCmZvciAoaSBpbiBsYXBwbHkoZ2VuZXNfdG9fc2hvdywgZnVuYzEsImRzMSIsZHMxZGF0YSkpCnsKICBtZXJnZV9leHByIDwtIHJiaW5kKG1lcmdlX2V4cHIsaSkKfQpmb3IgKGkgaW4gbGFwcGx5KGdlbmVzX3RvX3Nob3csIGZ1bmMxLCJkczIiLGRzMmRhdGEpKQp7CiAgbWVyZ2VfZXhwciA8LSByYmluZChtZXJnZV9leHByLGkpCn0KCgpyb3duYW1lcyhtZXJnZV9leHByKSA8LSBOVUxMCgoKCkRhdGFfc3VtbWFyeSA8LSBSbWlzYzo6c3VtbWFyeVNFKG1lcmdlX2V4cHIsIG1lYXN1cmV2YXI9ImV4cHIiLCBncm91cHZhcnM9Yygic2FtcGxlIiwiZ2VuZSIpKQpoZWFkKERhdGFfc3VtbWFyeSkKCmdnb2JqIDwtIGdncGxvdChtZXJnZV9leHByLGFlcyh4ID0gZ2VuZSwgeSA9IGV4cHIsZmlsbCA9IHNhbXBsZSkpICsKICBnZW9tX3NwbGl0X3Zpb2xpbih0cmltPSBGLCBjb2xvcj0id2hpdGUiLCBzY2FsZSA9ICJhcmVhIikgKyAKICBnZW9tX3BvaW50KGRhdGEgPSBEYXRhX3N1bW1hcnksYWVzKHggPSBnZW5lLCB5PSBleHByKSwgcGNoPTE5LAogICAgICAgICAgICAgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoMC4yKSxzaXplPSAxKSArICPnu5jliLblnYflgLzkvY3nva4KICBnZW9tX2Vycm9yYmFyKGRhdGEgPSBEYXRhX3N1bW1hcnksIGFlcyh5bWluID0gZXhwci1jaSwgeW1heD0gZXhwcitjaSksIAogICAgICAgICAgICAgICAgd2lkdGg9IDAuMDUsIAogICAgICAgICAgICAgICAgcG9zaXRpb249IHBvc2l0aW9uX2RvZGdlKDAuMiksICPor6/lt67nur/kvY3nva7vvIzlkozlnYflgLzkvY3nva7nm7jljLnphY0KICAgICAgICAgICAgICAgIGNvbG9yPSJibGFjayIsCiAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNywKICAgICAgICAgICAgICAgIHNpemU9IDAuNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNiMWQ2ZmIiLCAiI2ZkOTk5OSIpKSsgCiAgbGFicyh5PSgiTG9nMiBleHByZXNzaW9uIikseD1OVUxMLHRpdGxlID0gIlNwbGl0IHZpb2xpbiIpICsgCiAgdGhlbWVfY2xhc3NpYygpKyBteXRoZW1lICsgc3RhdF9jb21wYXJlX21lYW5zKGFlcyhncm91cCA9IHNhbXBsZSksCiAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gInAuZm9ybWF0IiwKICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gIndpbGNveC50ZXN0IiwKICAgICAgICAgICAgICAgICAgICAgbGFiZWwueSA9IG1heChtZXJnZV9leHByJGV4cHIpLAogICAgICAgICAgICAgICAgICAgICAgaGlkZS5ucyA9IEYpCmdnc2F2ZSgidW5kczJ0b2RzMS5zdmciLCBkZXZpY2UgPSBzdmcsIHBsb3QgPSBnZ29iaiwgaGVpZ2h0ID0gNiwgd2lkdGggPSAxMCkKCmBgYAojIOebkeedo+iBmuexu+e7k+aenAojIyDov5nph4zkvb/nlKhudW1lcmlj55qE5YiG576kCmBgYHtyfQpkczJGYk0gPC0gc3Vic2V0KGRzMixpZGVudCA9ICIxIikKZHMxRmJNIDwtIHN1YnNldChkczEsaWRlbnQgPSAiMSIpCgpkczJkYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMkZiTSx0eXBlID0gImRhdGEiKQpkczFkYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMUZiTSx0eXBlID0gImRhdGEiKQoKIyBkczJleHByIDwtIGRhdGEuZnJhbWUoZXhwciA9IGRzMmRhdGFbIkRDTiIsXSwgc2FtcGxlID0gImRzMiIsIGdlbmUgPSAiRENOIikKIyByb3duYW1lcyhkczJleHByKSA8LSBOVUxMCiMgZHMxZXhwciA8LSBkYXRhLmZyYW1lKGV4cHIgPSBkczFkYXRhWyJEQ04iLF0sIHNhbXBsZSA9ICJkczEiKQojIHJvd25hbWVzKGRzMWV4cHIpIDwtIE5VTEwKIyBtZXJnZV9leHByIDwtIHJiaW5kKGRzMmV4cHIsZHMxZXhwcikKCmdlbmVzX3RvX3Nob3cgPC0gYygiRENOIiwiTFVNIiwiTU1QMiIsIkFDVEEyIiwiVE5GUlNGMTFCIiwiRkJMTjEiKQoKZnVuYzEgPC0gZnVuY3Rpb24oZ2VuZSwgc2FtcGxlLCBkYXRhYmxlKXsKICBkYXRhLmZyYW1lKGV4cHIgPSBkYXRhYmxlW2dlbmUsXSwgc2FtcGxlID0gc2FtcGxlLCBnZW5lID0gZ2VuZSkKfQoKbWVyZ2VfZXhwciA8LSBkYXRhLmZyYW1lKCkKCmZvciAoaSBpbiBsYXBwbHkoZ2VuZXNfdG9fc2hvdywgZnVuYzEsImRzMSIsZHMxZGF0YSkpCnsKICBtZXJnZV9leHByIDwtIHJiaW5kKG1lcmdlX2V4cHIsaSkKfQpmb3IgKGkgaW4gbGFwcGx5KGdlbmVzX3RvX3Nob3csIGZ1bmMxLCJkczIiLGRzMmRhdGEpKQp7CiAgbWVyZ2VfZXhwciA8LSByYmluZChtZXJnZV9leHByLGkpCn0KCgpyb3duYW1lcyhtZXJnZV9leHByKSA8LSBOVUxMCgpEYXRhX3N1bW1hcnkgPC0gUm1pc2M6OnN1bW1hcnlTRShtZXJnZV9leHByLCBtZWFzdXJldmFyPSJleHByIiwgZ3JvdXB2YXJzPWMoInNhbXBsZSIsImdlbmUiKSkKaGVhZChEYXRhX3N1bW1hcnkpCgpnZ29iaiA8LSBnZ3Bsb3QobWVyZ2VfZXhwcixhZXMoeCA9IGdlbmUsIHkgPSBleHByLGZpbGwgPSBzYW1wbGUpKSArCiAgZ2VvbV9zcGxpdF92aW9saW4odHJpbT0gRiwgY29sb3I9IndoaXRlIiwgc2NhbGUgPSAiYXJlYSIpICsgCiAgZ2VvbV9wb2ludChkYXRhID0gRGF0YV9zdW1tYXJ5LGFlcyh4ID0gZ2VuZSwgeT0gZXhwciksIHBjaD0xOSwKICAgICAgICAgICAgIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKDAuMiksc2l6ZT0gMSkgKyAj57uY5Yi25Z2H5YC85L2N572uCiAgZ2VvbV9lcnJvcmJhcihkYXRhID0gRGF0YV9zdW1tYXJ5LCBhZXMoeW1pbiA9IGV4cHItY2ksIHltYXg9IGV4cHIrY2kpLCAKICAgICAgICAgICAgICAgIHdpZHRoPSAwLjA1LCAKICAgICAgICAgICAgICAgIHBvc2l0aW9uPSBwb3NpdGlvbl9kb2RnZSgwLjIpLCAj6K+v5beu57q/5L2N572u77yM5ZKM5Z2H5YC85L2N572u55u45Yy56YWNCiAgICAgICAgICAgICAgICBjb2xvcj0iYmxhY2siLAogICAgICAgICAgICAgICAgYWxwaGEgPSAwLjcsCiAgICAgICAgICAgICAgICBzaXplPSAwLjUpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjYjFkNmZiIiwgIiNmZDk5OTkiKSkrIAogIGxhYnMoeT0oIkxvZzIgZXhwcmVzc2lvbiIpLHg9TlVMTCx0aXRsZSA9ICJTcGxpdCB2aW9saW4iKSArIAogIHRoZW1lX2NsYXNzaWMoKSsgbXl0aGVtZSArIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBzYW1wbGUpLAogICAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICJwLmZvcm1hdCIsCiAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJ3aWxjb3gudGVzdCIsCiAgICAgICAgICAgICAgICAgICAgIGxhYmVsLnkgPSBtYXgobWVyZ2VfZXhwciRleHByKSwKICAgICAgICAgICAgICAgICAgICAgIGhpZGUubnMgPSBGKQoKZ2dzYXZlKCJzdXBkczJ0b2RzMS5zdmciLCBkZXZpY2UgPSBzdmcsIHBsb3QgPSBnZ29iaiwgaGVpZ2h0ID0gNiwgd2lkdGggPSAxMCkKYGBgCgojIGRzMiAtPiBkczAKIyMg5peg55uR552j6IGa57G7CmBgYHtyfQpkczJGYk0gPC0gc3Vic2V0KGRzMixpZGVudCA9ICIxIikKZHMwRmJNIDwtIHN1YnNldChkczAsaWRlbnQgPSAiRmlicm9teW9jeXRlIikKCmRzMmRhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMyRmJNLHR5cGUgPSAiZGF0YSIpCmRzMGRhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMwRmJNLHR5cGUgPSAiZGF0YSIpCgpnZW5lc190b19zaG93IDwtIGMoIkRDTiIsIkxVTSIsIk1NUDIiLCJBQ1RBMiIsIlRORlJTRjExQiIsIkZCTE4xIikKCgptZXJnZV9leHByIDwtIGRhdGEuZnJhbWUoKQoKZm9yIChpIGluIGxhcHBseShnZW5lc190b19zaG93LCBmdW5jMSwiZHMwIixkczBkYXRhKSkKewogIG1lcmdlX2V4cHIgPC0gcmJpbmQobWVyZ2VfZXhwcixpKQp9CmZvciAoaSBpbiBsYXBwbHkoZ2VuZXNfdG9fc2hvdywgZnVuYzEsImRzMiIsZHMyZGF0YSkpCnsKICBtZXJnZV9leHByIDwtIHJiaW5kKG1lcmdlX2V4cHIsaSkKfQoKcm93bmFtZXMobWVyZ2VfZXhwcikgPC0gTlVMTAoKRGF0YV9zdW1tYXJ5IDwtIFJtaXNjOjpzdW1tYXJ5U0UobWVyZ2VfZXhwciwgbWVhc3VyZXZhcj0iZXhwciIsIGdyb3VwdmFycz1jKCJzYW1wbGUiLCJnZW5lIikpCmhlYWQoRGF0YV9zdW1tYXJ5KQoKZ2dvYmogPC0gZ2dwbG90KG1lcmdlX2V4cHIsYWVzKHggPSBnZW5lLCB5ID0gZXhwcixmaWxsID0gc2FtcGxlKSkgKwogIGdlb21fc3BsaXRfdmlvbGluKHRyaW09IEYsIGNvbG9yPSJ3aGl0ZSIsIHNjYWxlID0gImFyZWEiKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IERhdGFfc3VtbWFyeSxhZXMoeCA9IGdlbmUsIHk9IGV4cHIpLCBwY2g9MTksCiAgICAgICAgICAgICBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSgwLjIpLHNpemU9IDEpICsgI+e7mOWItuWdh+WAvOS9jee9rgogIGdlb21fZXJyb3JiYXIoZGF0YSA9IERhdGFfc3VtbWFyeSwgYWVzKHltaW4gPSBleHByLWNpLCB5bWF4PSBleHByK2NpKSwgCiAgICAgICAgICAgICAgICB3aWR0aD0gMC4wNSwgCiAgICAgICAgICAgICAgICBwb3NpdGlvbj0gcG9zaXRpb25fZG9kZ2UoMC4yKSwgI+ivr+W3rue6v+S9jee9ru+8jOWSjOWdh+WAvOS9jee9ruebuOWMuemFjQogICAgICAgICAgICAgICAgY29sb3I9ImJsYWNrIiwKICAgICAgICAgICAgICAgIGFscGhhID0gMC43LAogICAgICAgICAgICAgICAgc2l6ZT0gMC41KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI2IxZDZmYiIsICIjZmQ5OTk5IikpKyAKICBsYWJzKHk9KCJMb2cyIGV4cHJlc3Npb24iKSx4PU5VTEwsdGl0bGUgPSAiU3BsaXQgdmlvbGluIikgKyAKICB0aGVtZV9jbGFzc2ljKCkrIG15dGhlbWUgKyBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gc2FtcGxlKSwKICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSAicC5mb3JtYXQiLAogICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAid2lsY294LnRlc3QiLAogICAgICAgICAgICAgICAgICAgICBsYWJlbC55ID0gbWF4KG1lcmdlX2V4cHIkZXhwciksCiAgICAgICAgICAgICAgICAgICAgICBoaWRlLm5zID0gRikKCmdnc2F2ZSgidW5kczJ0b2RzMC5zdmciLCBkZXZpY2UgPSBzdmcsIHBsb3QgPSBnZ29iaiwgaGVpZ2h0ID0gNiwgd2lkdGggPSAxMCkKYGBgCgojIyDnm5HnnaPogZrnsbsKCmBgYHtyfQpJZGVudHMoZHMwKSA8LSBkczAkc3VwY2x1c3RlcmluZwpkczJGYk0gPC0gc3Vic2V0KGRzMixpZGVudCA9ICIxIikKZHMwRmJNIDwtIHN1YnNldChkczAsaWRlbnQgPSAiMSIpCgpkczJkYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMkZiTSx0eXBlID0gImRhdGEiKQpkczBkYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMEZiTSx0eXBlID0gImRhdGEiKQoKZ2VuZXNfdG9fc2hvdyA8LSBjKCJEQ04iLCJMVU0iLCJNTVAyIiwiQUNUQTIiLCJUTkZSU0YxMUIiLCJGQkxOMSIpCgptZXJnZV9leHByIDwtIGRhdGEuZnJhbWUoKQoKZm9yIChpIGluIGxhcHBseShnZW5lc190b19zaG93LCBmdW5jMSwiZHMwIixkczBkYXRhKSkKewogIG1lcmdlX2V4cHIgPC0gcmJpbmQobWVyZ2VfZXhwcixpKQp9CmZvciAoaSBpbiBsYXBwbHkoZ2VuZXNfdG9fc2hvdywgZnVuYzEsImRzMiIsZHMyZGF0YSkpCnsKICBtZXJnZV9leHByIDwtIHJiaW5kKG1lcmdlX2V4cHIsaSkKfQoKcm93bmFtZXMobWVyZ2VfZXhwcikgPC0gTlVMTAoKRGF0YV9zdW1tYXJ5IDwtIFJtaXNjOjpzdW1tYXJ5U0UobWVyZ2VfZXhwciwgbWVhc3VyZXZhcj0iZXhwciIsIGdyb3VwdmFycz1jKCJzYW1wbGUiLCJnZW5lIikpCmhlYWQoRGF0YV9zdW1tYXJ5KQoKZ2dvYmogPC0gZ2dwbG90KG1lcmdlX2V4cHIsYWVzKHggPSBnZW5lLCB5ID0gZXhwcixmaWxsID0gc2FtcGxlKSkgKwogIGdlb21fc3BsaXRfdmlvbGluKHRyaW09IEYsIGNvbG9yPSJ3aGl0ZSIsIHNjYWxlID0gImFyZWEiKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IERhdGFfc3VtbWFyeSxhZXMoeCA9IGdlbmUsIHk9IGV4cHIpLCBwY2g9MTksCiAgICAgICAgICAgICBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSgwLjIpLHNpemU9IDEpICsgI+e7mOWItuWdh+WAvOS9jee9rgogIGdlb21fZXJyb3JiYXIoZGF0YSA9IERhdGFfc3VtbWFyeSwgYWVzKHltaW4gPSBleHByLWNpLCB5bWF4PSBleHByK2NpKSwgCiAgICAgICAgICAgICAgICB3aWR0aD0gMC4wNSwgCiAgICAgICAgICAgICAgICBwb3NpdGlvbj0gcG9zaXRpb25fZG9kZ2UoMC4yKSwgI+ivr+W3rue6v+S9jee9ru+8jOWSjOWdh+WAvOS9jee9ruebuOWMuemFjQogICAgICAgICAgICAgICAgY29sb3I9ImJsYWNrIiwKICAgICAgICAgICAgICAgIGFscGhhID0gMC43LAogICAgICAgICAgICAgICAgc2l6ZT0gMC41KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI2IxZDZmYiIsICIjZmQ5OTk5IikpKyAKICBsYWJzKHk9KCJMb2cyIGV4cHJlc3Npb24iKSx4PU5VTEwsdGl0bGUgPSAiU3BsaXQgdmlvbGluIikgKyAKICB0aGVtZV9jbGFzc2ljKCkrIG15dGhlbWUgKyBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gc2FtcGxlKSwKICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSAicC5mb3JtYXQiLAogICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAid2lsY294LnRlc3QiLAogICAgICAgICAgICAgICAgICAgICBsYWJlbC55ID0gbWF4KG1lcmdlX2V4cHIkZXhwciksCiAgICAgICAgICAgICAgICAgICAgICBoaWRlLm5zID0gRikKCmdnc2F2ZSgic3VwZHMydG9kczAuc3ZnIiwgZGV2aWNlID0gc3ZnLCBwbG90ID0gZ2dvYmosIGhlaWdodCA9IDYsIHdpZHRoID0gMTApCmBgYAoKIyBkczAgLT4gZHMyCiMj5peg55uR552j6IGa57G7CmBgYHtyfQpkczIgPC0gcmVhZFJEUygiZHMyLnJkcyIpCklkZW50cyhkczApIDwtIGRzMCRDbGFzc2lmaWNhdGlvbjEKZHMyRmJNIDwtIHN1YnNldChkczIsaWRlbnQgPSAiRmlicm9teW9jeXRlIikKZHMwRmJNIDwtIHN1YnNldChkczAsaWRlbnQgPSAiRmlicm9teW9jeXRlIikKCmRzMmRhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMyRmJNLHR5cGUgPSAiZGF0YSIpCmRzMGRhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMwRmJNLHR5cGUgPSAiZGF0YSIpCgojIGdlbmVzX3RvX3Nob3cgPC0gYygiSUdGQlAyIiwiTUdQIiwiTVlIMTEiLCJEQ04iLCJUTkZSU0YxMUIiKQpnZW5lc190b19zaG93IDwtIGMoIkRDTiIsIkxVTSIsIk1NUDIiLCJBQ1RBMiIsIlRORlJTRjExQiIsIkZCTE4xIikKCgoKbWVyZ2VfZXhwciA8LSBkYXRhLmZyYW1lKCkKCmZvciAoaSBpbiBsYXBwbHkoZ2VuZXNfdG9fc2hvdywgZnVuYzEsImRzMCIsZHMwZGF0YSkpCnsKICBtZXJnZV9leHByIDwtIHJiaW5kKG1lcmdlX2V4cHIsaSkKfQpmb3IgKGkgaW4gbGFwcGx5KGdlbmVzX3RvX3Nob3csIGZ1bmMxLCJkczIiLGRzMmRhdGEpKQp7CiAgbWVyZ2VfZXhwciA8LSByYmluZChtZXJnZV9leHByLGkpCn0KCnJvd25hbWVzKG1lcmdlX2V4cHIpIDwtIE5VTEwKCkRhdGFfc3VtbWFyeSA8LSBSbWlzYzo6c3VtbWFyeVNFKG1lcmdlX2V4cHIsIG1lYXN1cmV2YXI9ImV4cHIiLCBncm91cHZhcnM9Yygic2FtcGxlIiwiZ2VuZSIpKQpoZWFkKERhdGFfc3VtbWFyeSkKCmdnb2JqIDwtIGdncGxvdChtZXJnZV9leHByLGFlcyh4ID0gZ2VuZSwgeSA9IGV4cHIsZmlsbCA9IHNhbXBsZSkpICsKICBnZW9tX3NwbGl0X3Zpb2xpbih0cmltPSBGLCBjb2xvcj0id2hpdGUiLCBzY2FsZSA9ICJhcmVhIikgKyAKICBnZW9tX3BvaW50KGRhdGEgPSBEYXRhX3N1bW1hcnksYWVzKHggPSBnZW5lLCB5PSBleHByKSwgcGNoPTE5LAogICAgICAgICAgICAgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoMC4yKSxzaXplPSAxKSArICPnu5jliLblnYflgLzkvY3nva4KICBnZW9tX2Vycm9yYmFyKGRhdGEgPSBEYXRhX3N1bW1hcnksIGFlcyh5bWluID0gZXhwci1jaSwgeW1heD0gZXhwcitjaSksIAogICAgICAgICAgICAgICAgd2lkdGg9IDAuMDUsIAogICAgICAgICAgICAgICAgcG9zaXRpb249IHBvc2l0aW9uX2RvZGdlKDAuMiksICPor6/lt67nur/kvY3nva7vvIzlkozlnYflgLzkvY3nva7nm7jljLnphY0KICAgICAgICAgICAgICAgIGNvbG9yPSJibGFjayIsCiAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNywKICAgICAgICAgICAgICAgIHNpemU9IDAuNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNiMWQ2ZmIiLCAiI2ZkOTk5OSIpKSsgCiAgbGFicyh5PSgiTG9nMiBleHByZXNzaW9uIikseD1OVUxMLHRpdGxlID0gIlNwbGl0IHZpb2xpbiIpICsgCiAgdGhlbWVfY2xhc3NpYygpKyBteXRoZW1lICsgc3RhdF9jb21wYXJlX21lYW5zKGFlcyhncm91cCA9IHNhbXBsZSksCiAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gInAuZm9ybWF0IiwKICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gIndpbGNveC50ZXN0IiwKICAgICAgICAgICAgICAgICAgICAgbGFiZWwueSA9IG1heChtZXJnZV9leHByJGV4cHIpLAogICAgICAgICAgICAgICAgICAgICAgaGlkZS5ucyA9IEYpCgpnZ3NhdmUoIjJfdW5kczB0b2RzMi5zdmciLCBkZXZpY2UgPSBzdmcsIHBsb3QgPSBnZ29iaiwgaGVpZ2h0ID0gNiwgd2lkdGggPSAxMCkKYGBgCgojIyDnm5HnnaPogZrnsbsKZHMwIC0+IGRzMgpgYGB7cn0KCmRzMkZiTSA8LSBzdWJzZXQoZHMyLGlkZW50ID0gIjIiKQpkczBGYk0gPC0gc3Vic2V0KGRzMCxpZGVudCA9ICIyIikKCmRzMmRhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMyRmJNLHR5cGUgPSAiZGF0YSIpCmRzMGRhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMwRmJNLHR5cGUgPSAiZGF0YSIpCgpnZW5lc190b19zaG93IDwtIGMoIklHRkJQMiIsIk1HUCIsIk1ZSDExIiwiRENOIiwiVE5GUlNGMTFCIikKIyBnZW5lc190b19zaG93IDwtIGMoIkRDTiIsIkxVTSIsIk1NUDIiLCJBQ1RBMiIsIlRORlJTRjExQiIsIkZCTE4xIikKCm1lcmdlX2V4cHIgPC0gZGF0YS5mcmFtZSgpCgpmb3IgKGkgaW4gbGFwcGx5KGdlbmVzX3RvX3Nob3csIGZ1bmMxLCJkczAiLGRzMGRhdGEpKQp7CiAgbWVyZ2VfZXhwciA8LSByYmluZChtZXJnZV9leHByLGkpCn0KZm9yIChpIGluIGxhcHBseShnZW5lc190b19zaG93LCBmdW5jMSwiZHMyIixkczJkYXRhKSkKewogIG1lcmdlX2V4cHIgPC0gcmJpbmQobWVyZ2VfZXhwcixpKQp9Cgpyb3duYW1lcyhtZXJnZV9leHByKSA8LSBOVUxMCgpEYXRhX3N1bW1hcnkgPC0gUm1pc2M6OnN1bW1hcnlTRShtZXJnZV9leHByLCBtZWFzdXJldmFyPSJleHByIiwgZ3JvdXB2YXJzPWMoInNhbXBsZSIsImdlbmUiKSkKaGVhZChEYXRhX3N1bW1hcnkpCgpnZ29iaiA8LSBnZ3Bsb3QobWVyZ2VfZXhwcixhZXMoeCA9IGdlbmUsIHkgPSBleHByLGZpbGwgPSBzYW1wbGUpKSArCiAgZ2VvbV9zcGxpdF92aW9saW4odHJpbT0gRiwgY29sb3I9IndoaXRlIiwgc2NhbGUgPSAiYXJlYSIpICsgCiAgZ2VvbV9wb2ludChkYXRhID0gRGF0YV9zdW1tYXJ5LGFlcyh4ID0gZ2VuZSwgeT0gZXhwciksIHBjaD0xOSwKICAgICAgICAgICAgIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKDAuMiksc2l6ZT0gMSkgKyAj57uY5Yi25Z2H5YC85L2N572uCiAgZ2VvbV9lcnJvcmJhcihkYXRhID0gRGF0YV9zdW1tYXJ5LCBhZXMoeW1pbiA9IGV4cHItY2ksIHltYXg9IGV4cHIrY2kpLCAKICAgICAgICAgICAgICAgIHdpZHRoPSAwLjA1LCAKICAgICAgICAgICAgICAgIHBvc2l0aW9uPSBwb3NpdGlvbl9kb2RnZSgwLjIpLCAj6K+v5beu57q/5L2N572u77yM5ZKM5Z2H5YC85L2N572u55u45Yy56YWNCiAgICAgICAgICAgICAgICBjb2xvcj0iYmxhY2siLAogICAgICAgICAgICAgICAgYWxwaGEgPSAwLjcsCiAgICAgICAgICAgICAgICBzaXplPSAwLjUpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjYjFkNmZiIiwgIiNmZDk5OTkiKSkrIAogIGxhYnMoeT0oIkxvZzIgZXhwcmVzc2lvbiIpLHg9TlVMTCx0aXRsZSA9ICJTcGxpdCB2aW9saW4iKSArIAogIHRoZW1lX2NsYXNzaWMoKSsgbXl0aGVtZSArIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBzYW1wbGUpLAogICAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICJwLmZvcm1hdCIsCiAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJ3aWxjb3gudGVzdCIsCiAgICAgICAgICAgICAgICAgICAgIGxhYmVsLnkgPSBtYXgobWVyZ2VfZXhwciRleHByKSwKICAgICAgICAgICAgICAgICAgICAgIGhpZGUubnMgPSBGKQpnZ29iagpnZ3NhdmUoInN1cGRzMHRvZHMyLnN2ZyIsIGRldmljZSA9IHN2ZywgcGxvdCA9IGdnb2JqLCBoZWlnaHQgPSA2LCB3aWR0aCA9IDEwKQpgYGAKCgoKIyBkczAgLT4gZHMxCiMj5peg55uR552j6IGa57G7CmBgYHtyfQpJZGVudHMoZHMxKSA8LSBkczEkQ2xhc3NpZmljYXRpb24xCklkZW50cyhkczApIDwtIGRzMCRDbGFzc2lmaWNhdGlvbjEKZHMxRmJNIDwtIHN1YnNldChkczEsaWRlbnQgPSAiRmlicm9teW9jeXRlIikKZHMwRmJNIDwtIHN1YnNldChkczAsaWRlbnQgPSAiRmlicm9teW9jeXRlIikKCmRzMWRhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMxRmJNLHR5cGUgPSAiZGF0YSIpCmRzMGRhdGEgPC0gZ2V0X2RhdGFfdGFibGUoZHMwRmJNLHR5cGUgPSAiZGF0YSIpCgojIGdlbmVzX3RvX3Nob3cgPC0gYygiSUdGQlAyIiwiTUdQIiwiTVlIMTEiLCJEQ04iLCJUTkZSU0YxMUIiKQpnZW5lc190b19zaG93IDwtIGMoIkRDTiIsIkxVTSIsIk1NUDIiLCJBQ1RBMiIsIlRORlJTRjExQiIsIkZCTE4xIikKCmZ1bmMgPC0gZnVuY3Rpb24oZ2VuZSwgc2FtcGxlLCBkYXRhYmxlKXsKICBkYXRhLmZyYW1lKGV4cHIgPSBkYXRhYmxlW2dlbmUsXSwgc2FtcGxlID0gc2FtcGxlLCBnZW5lID0gZ2VuZSkKfQoKbWVyZ2VfZXhwciA8LSBkYXRhLmZyYW1lKCkKCmZvciAoaSBpbiBsYXBwbHkoZ2VuZXNfdG9fc2hvdywgZnVuYywiZHMwIixkczBkYXRhKSkKewogIG1lcmdlX2V4cHIgPC0gcmJpbmQobWVyZ2VfZXhwcixpKQp9CmZvciAoaSBpbiBsYXBwbHkoZ2VuZXNfdG9fc2hvdywgZnVuYywiZHMxIixkczFkYXRhKSkKewogIG1lcmdlX2V4cHIgPC0gcmJpbmQobWVyZ2VfZXhwcixpKQp9Cgpyb3duYW1lcyhtZXJnZV9leHByKSA8LSBOVUxMCgpEYXRhX3N1bW1hcnkgPC0gUm1pc2M6OnN1bW1hcnlTRShtZXJnZV9leHByLCBtZWFzdXJldmFyPSJleHByIiwgZ3JvdXB2YXJzPWMoInNhbXBsZSIsImdlbmUiKSkKaGVhZChEYXRhX3N1bW1hcnkpCgpnZ29iaiA8LSBnZ3Bsb3QobWVyZ2VfZXhwcixhZXMoeCA9IGdlbmUsIHkgPSBleHByLGZpbGwgPSBzYW1wbGUpKSArCiAgZ2VvbV9zcGxpdF92aW9saW4odHJpbT0gRiwgY29sb3I9IndoaXRlIiwgc2NhbGUgPSAiYXJlYSIpICsgCiAgZ2VvbV9wb2ludChkYXRhID0gRGF0YV9zdW1tYXJ5LGFlcyh4ID0gZ2VuZSwgeT0gZXhwciksIHBjaD0xOSwKICAgICAgICAgICAgIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKDAuMiksc2l6ZT0gMSkgKyAj57uY5Yi25Z2H5YC85L2N572uCiAgZ2VvbV9lcnJvcmJhcihkYXRhID0gRGF0YV9zdW1tYXJ5LCBhZXMoeW1pbiA9IGV4cHItY2ksIHltYXg9IGV4cHIrY2kpLCAKICAgICAgICAgICAgICAgIHdpZHRoPSAwLjA1LCAKICAgICAgICAgICAgICAgIHBvc2l0aW9uPSBwb3NpdGlvbl9kb2RnZSgwLjIpLCAj6K+v5beu57q/5L2N572u77yM5ZKM5Z2H5YC85L2N572u55u45Yy56YWNCiAgICAgICAgICAgICAgICBjb2xvcj0iYmxhY2siLAogICAgICAgICAgICAgICAgYWxwaGEgPSAwLjcsCiAgICAgICAgICAgICAgICBzaXplPSAwLjUpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjYjFkNmZiIiwgIiNmZDk5OTkiKSkrIAogIGxhYnMoeT0oIkxvZzIgZXhwcmVzc2lvbiIpLHg9TlVMTCx0aXRsZSA9ICJTcGxpdCB2aW9saW4iKSArIAogIHRoZW1lX2NsYXNzaWMoKSsgbXl0aGVtZSArIHN0YXRfY29tcGFyZV9tZWFucyhhZXMoZ3JvdXAgPSBzYW1wbGUpLAogICAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICJwLmZvcm1hdCIsCiAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJ3aWxjb3gudGVzdCIsCiAgICAgICAgICAgICAgICAgICAgIGxhYmVsLnkgPSBtYXgobWVyZ2VfZXhwciRleHByKSwKICAgICAgICAgICAgICAgICAgICAgIGhpZGUubnMgPSBGKQpnZ29iagpnZ3NhdmUoIjJfdW5kczB0b2RzMS5zdmciLCBkZXZpY2UgPSBzdmcsIHBsb3QgPSBnZ29iaiwgaGVpZ2h0ID0gNiwgd2lkdGggPSAxMCkKYGBgCgojIyDnm5HnnaPogZrnsbsKZHMwIC0+IGRzMQpgYGB7cn0KSWRlbnRzKGRzMCkgPC0gZHMwJHNldXJhdF9jbHVzdGVycwpkczFGYk0gPC0gc3Vic2V0KGRzMSxpZGVudCA9ICIyIikKZHMwRmJNIDwtIHN1YnNldChkczAsaWRlbnQgPSAiMiIpCgpkczFkYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMUZiTSx0eXBlID0gImRhdGEiKQpkczBkYXRhIDwtIGdldF9kYXRhX3RhYmxlKGRzMEZiTSx0eXBlID0gImRhdGEiKQoKIyBnZW5lc190b19zaG93IDwtIGMoIklHRkJQMiIsIk1HUCIsIk1ZSDExIiwiRENOIiwiVE5GUlNGMTFCIikKZ2VuZXNfdG9fc2hvdyA8LSBjKCJEQ04iLCJMVU0iLCJNTVAyIiwiQUNUQTIiLCJUTkZSU0YxMUIiLCJGQkxOMSIpCgptZXJnZV9leHByIDwtIGRhdGEuZnJhbWUoKQoKZm9yIChpIGluIGxhcHBseShnZW5lc190b19zaG93LCBmdW5jMSwiZHMwIixkczBkYXRhKSkKewogIG1lcmdlX2V4cHIgPC0gcmJpbmQobWVyZ2VfZXhwcixpKQp9CmZvciAoaSBpbiBsYXBwbHkoZ2VuZXNfdG9fc2hvdywgZnVuYzEsImRzMSIsZHMxZGF0YSkpCnsKICBtZXJnZV9leHByIDwtIHJiaW5kKG1lcmdlX2V4cHIsaSkKfQoKcm93bmFtZXMobWVyZ2VfZXhwcikgPC0gTlVMTAoKRGF0YV9zdW1tYXJ5IDwtIFJtaXNjOjpzdW1tYXJ5U0UobWVyZ2VfZXhwciwgbWVhc3VyZXZhcj0iZXhwciIsIGdyb3VwdmFycz1jKCJzYW1wbGUiLCJnZW5lIikpCmhlYWQoRGF0YV9zdW1tYXJ5KQoKZ2dvYmogPC0gZ2dwbG90KG1lcmdlX2V4cHIsYWVzKHggPSBnZW5lLCB5ID0gZXhwcixmaWxsID0gc2FtcGxlKSkgKwogIGdlb21fc3BsaXRfdmlvbGluKHRyaW09IEYsIGNvbG9yPSJ3aGl0ZSIsIHNjYWxlID0gImFyZWEiKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IERhdGFfc3VtbWFyeSxhZXMoeCA9IGdlbmUsIHk9IGV4cHIpLCBwY2g9MTksCiAgICAgICAgICAgICBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSgwLjIpLHNpemU9IDEpICsgI+e7mOWItuWdh+WAvOS9jee9rgogIGdlb21fZXJyb3JiYXIoZGF0YSA9IERhdGFfc3VtbWFyeSwgYWVzKHltaW4gPSBleHByLWNpLCB5bWF4PSBleHByK2NpKSwgCiAgICAgICAgICAgICAgICB3aWR0aD0gMC4wNSwgCiAgICAgICAgICAgICAgICBwb3NpdGlvbj0gcG9zaXRpb25fZG9kZ2UoMC4yKSwgI+ivr+W3rue6v+S9jee9ru+8jOWSjOWdh+WAvOS9jee9ruebuOWMuemFjQogICAgICAgICAgICAgICAgY29sb3I9ImJsYWNrIiwKICAgICAgICAgICAgICAgIGFscGhhID0gMC43LAogICAgICAgICAgICAgICAgc2l6ZT0gMC41KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI2IxZDZmYiIsICIjZmQ5OTk5IikpKyAKICBsYWJzKHk9KCJMb2cyIGV4cHJlc3Npb24iKSx4PU5VTEwsdGl0bGUgPSAiU3BsaXQgdmlvbGluIikgKyAKICB0aGVtZV9jbGFzc2ljKCkrIG15dGhlbWUgKyBzdGF0X2NvbXBhcmVfbWVhbnMoYWVzKGdyb3VwID0gc2FtcGxlKSwKICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSAicC5mb3JtYXQiLAogICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAid2lsY294LnRlc3QiLAogICAgICAgICAgICAgICAgICAgICBsYWJlbC55ID0gbWF4KG1lcmdlX2V4cHIkZXhwciksCiAgICAgICAgICAgICAgICAgICAgICBoaWRlLm5zID0gRikKZ2dvYmoKZ2dzYXZlKCIyX3N1cGRzMHRvZHMxLnN2ZyIsIGRldmljZSA9IHN2ZywgcGxvdCA9IGdnb2JqLCBoZWlnaHQgPSA2LCB3aWR0aCA9IDEwKQpgYGAKCgoKYGBge3J9Cm15dGhlbWUgPC0gdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsY29sb3I9ImJsYWNrIixoanVzdCA9IDAuNSksCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLGNvbG9yID0iYmxhY2siKSwgCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPSAxMixjb2xvciA9ICJibGFjayIpLAogICAgICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxICksCiAgICAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQ9ZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwKICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0gMTIpLAogICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGl0bGU9IGVsZW1lbnRfdGV4dChzaXplPSAxMikpIAoKIyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL2EvNDU2MTQ1NDcKR2VvbVNwbGl0VmlvbGluIDwtIGdncHJvdG8oIkdlb21TcGxpdFZpb2xpbiIsIEdlb21WaW9saW4sIGRyYXdfZ3JvdXAgPSBmdW5jdGlvbihzZWxmLCBkYXRhLCAuLi4sIGRyYXdfcXVhbnRpbGVzID0gTlVMTCl7CiAgZGF0YSA8LSB0cmFuc2Zvcm0oZGF0YSwgeG1pbnYgPSB4IC0gdmlvbGlud2lkdGggKiAoeCAtIHhtaW4pLCB4bWF4diA9IHggKyB2aW9saW53aWR0aCAqICh4bWF4IC0geCkpCiAgZ3JwIDwtIGRhdGFbMSwnZ3JvdXAnXQogIG5ld2RhdGEgPC0gcGx5cjo6YXJyYW5nZSh0cmFuc2Zvcm0oZGF0YSwgeCA9IGlmKGdycCUlMj09MSkgeG1pbnYgZWxzZSB4bWF4diksIGlmKGdycCUlMj09MSkgeSBlbHNlIC15KQogIG5ld2RhdGEgPC0gcmJpbmQobmV3ZGF0YVsxLCBdLCBuZXdkYXRhLCBuZXdkYXRhW25yb3cobmV3ZGF0YSksIF0sIG5ld2RhdGFbMSwgXSkKICBuZXdkYXRhW2MoMSxucm93KG5ld2RhdGEpLTEsbnJvdyhuZXdkYXRhKSksICd4J10gPC0gcm91bmQobmV3ZGF0YVsxLCAneCddKSAKICBpZiAobGVuZ3RoKGRyYXdfcXVhbnRpbGVzKSA+IDAgJiAhc2NhbGVzOjp6ZXJvX3JhbmdlKHJhbmdlKGRhdGEkeSkpKSB7CiAgICBzdG9waWZub3QoYWxsKGRyYXdfcXVhbnRpbGVzID49IDApLCBhbGwoZHJhd19xdWFudGlsZXMgPD0gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxKSkKICAgIHF1YW50aWxlcyA8LSBnZ3Bsb3QyOjo6Y3JlYXRlX3F1YW50aWxlX3NlZ21lbnRfZnJhbWUoZGF0YSwgZHJhd19xdWFudGlsZXMpCiAgICBhZXN0aGV0aWNzIDwtIGRhdGFbcmVwKDEsIG5yb3cocXVhbnRpbGVzKSksIHNldGRpZmYobmFtZXMoZGF0YSksIGMoIngiLCAieSIpKSwgZHJvcCA9IEZBTFNFXQogICAgYWVzdGhldGljcyRhbHBoYSA8LSByZXAoMSwgbnJvdyhxdWFudGlsZXMpKQogICAgYm90aCA8LSBjYmluZChxdWFudGlsZXMsIGFlc3RoZXRpY3MpCiAgICBxdWFudGlsZV9ncm9iIDwtIEdlb21QYXRoJGRyYXdfcGFuZWwoYm90aCwgLi4uKQogICAgZ2dwbG90Mjo6OmdnbmFtZSgiZ2VvbV9zcGxpdF92aW9saW4iLCBncmlkOjpncm9iVHJlZShHZW9tUG9seWdvbiRkcmF3X3BhbmVsKG5ld2RhdGEsIC4uLiksIHF1YW50aWxlX2dyb2IpKQogIH0KICBlbHNlIHsKICAgIGdncGxvdDI6OjpnZ25hbWUoImdlb21fc3BsaXRfdmlvbGluIiwgR2VvbVBvbHlnb24kZHJhd19wYW5lbChuZXdkYXRhLCAuLi4pKQogIH0KfSkKCmdlb21fc3BsaXRfdmlvbGluIDwtIGZ1bmN0aW9uIChtYXBwaW5nID0gTlVMTCwgZGF0YSA9IE5VTEwsIHN0YXQgPSAieWRlbnNpdHkiLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIsIC4uLiwgZHJhd19xdWFudGlsZXMgPSBOVUxMLCB0cmltID0gVFJVRSwgc2NhbGUgPSAiYXJlYSIsIG5hLnJtID0gRkFMU0UsIHNob3cubGVnZW5kID0gTkEsIGluaGVyaXQuYWVzID0gVFJVRSkgewogIGxheWVyKGRhdGEgPSBkYXRhLCBtYXBwaW5nID0gbWFwcGluZywgc3RhdCA9IHN0YXQsIGdlb20gPSBHZW9tU3BsaXRWaW9saW4sIHBvc2l0aW9uID0gcG9zaXRpb24sIHNob3cubGVnZW5kID0gc2hvdy5sZWdlbmQsIGluaGVyaXQuYWVzID0gaW5oZXJpdC5hZXMsIHBhcmFtcyA9IGxpc3QodHJpbSA9IHRyaW0sIHNjYWxlID0gc2NhbGUsIGRyYXdfcXVhbnRpbGVzID0gZHJhd19xdWFudGlsZXMsIG5hLnJtID0gbmEucm0sIC4uLikpCn0KYGBgCgoKCgoKQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkN0cmwrQWx0K0kqLgoKV2hlbiB5b3Ugc2F2ZSB0aGUgbm90ZWJvb2ssIGFuIEhUTUwgZmlsZSBjb250YWluaW5nIHRoZSBjb2RlIGFuZCBvdXRwdXQgd2lsbCBiZSBzYXZlZCBhbG9uZ3NpZGUgaXQgKGNsaWNrIHRoZSAqUHJldmlldyogYnV0dG9uIG9yIHByZXNzICpDdHJsK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuCgpUaGUgcHJldmlldyBzaG93cyB5b3UgYSByZW5kZXJlZCBIVE1MIGNvcHkgb2YgdGhlIGNvbnRlbnRzIG9mIHRoZSBlZGl0b3IuIENvbnNlcXVlbnRseSwgdW5saWtlICpLbml0KiwgKlByZXZpZXcqIGRvZXMgbm90IHJ1biBhbnkgUiBjb2RlIGNodW5rcy4gSW5zdGVhZCwgdGhlIG91dHB1dCBvZiB0aGUgY2h1bmsgd2hlbiBpdCB3YXMgbGFzdCBydW4gaW4gdGhlIGVkaXRvciBpcyBkaXNwbGF5ZWQuCg==